#include "../include/queue.h"
#include "../include/pool.h"
#include "../include/line.h"

void Delete_Queue(Object *self)
{
    Queue *queue = (Queue *)self;
    Globe_Free(queue->array);
}

void Queue_Init(Queue *self, u_int size)
{
    self->type = T_QUEUE;
    self->delete = Delete_Queue;
    self->line = NULL;
    self->size = size;
    self->array = Globe_Malloc(sizeof(TValue) * size);
    self->begin = 0;
    self->end = 0;
    self->length = 0;
}

void Queue_resize(Queue *self, u_int size)
{
    if (size < self->size)
    {
        if (self->begin < self->end && self->end < size)
        {
            self->array = Globe_Realloc(self->array, sizeof(TValue) * (size));
        }
        else
        {
            TValue *array = Globe_Malloc(sizeof(TValue) * size);
            for (u_int i = 0; i < self->length; i++)
            {
                array[i] = Queue_Get(self, i);
            }
            Globe_Free(self->array);
            self->array = array;
            self->begin = 0;
            self->end = self->length;
        }
    }
    else
    {
        self->array = Globe_Realloc(self->array, sizeof(TValue) * (size));
        if (self->begin > self->end)
        {
            _int length = self->length - self->end - 1;
            for (u_int i = 0; i < length; i++)
            {
                self->array[size - length + i] = self->array[self->begin + i];
            }
            self->begin = size - length;
        }
    }
    self->size = size;
}

void Queue_Push(Queue *self, TValue value)
{
    if (self->length == self->size)
        Queue_resize(self, (self->size << 2) + 1);
    if (self->length != 0)
    {
        if (self->end == self->size)
        {
            self->end = 0;
            self->array[self->end] = value;
        }
        else
        {
            self->end++;
            self->array[self->end] = value;
        }
    }
    else
    {
        self->array[self->begin = self->end = 0] = value;
    }
    self->length++;
}

TValue Queue_Pop(Queue *self)
{
    if (self->size > 4096 && self->length < (self->size >> 3))
    {
        Queue_resize(self, self->size >> 1);
    }
    _int index = self->end;
    if (self->end == 0)
        self->end = self->size - 1;
    else
        self->end--;
    self->length--;
    return self->array[index];
}

void Queue_Shift(Queue *self, TValue value)
{
    if (self->length == self->size)
        Queue_resize(self, (self->size << 2) + 1);
    if (self->length != 0)
    {
        if (self->begin == 0)
        {
            self->begin = self->size - 1;
            self->array[self->begin] = value;
        }
        else
        {
            self->begin--;
            self->array[self->begin] = value;
        }
    }
    else
    {
        self->array[self->begin = self->end = 0] = value;
    }
    self->length++;
}

TValue Queue_UnShift(Queue *self)
{
    if (self->size > 4096 && self->length < (self->size >> 3))
    {
        Queue_resize(self, self->size >> 1);
    }
    _int index = self->begin;
    if (index == self->size - 1)
        self->begin = 0;
    else
        self->begin++;
    self->length--;
    return self->array[index];
}

void Queue_Set(Queue *self, _int index, TValue value)
{
    if (index == self->length)
    {
        Queue_Push(self, value);
    }
    else
    {
        index = self->begin + index;
        if (index >= self->size)
            index -= self->size;
        self->array[index] = value;
    }
}

TValue Queue_Get(Queue *self, _int index)
{
    if (index >= self->length)
        return TNULL;
    index = self->begin + index;
    if (index >= self->size)
        return self->array[index - self->size];
    return self->array[index];
}

void Queue_Insert(Queue *self, _int index, TValue value)
{
    if (index << 1 < self->length)
    {
        Queue_Shift(self, value);
        for (u_int i = 0; i < index; i++)
        {
            Queue_Set(self, i, Queue_Get(self, i + 1));
        }
    }
    else
    {
        Queue_Push(self, value);
        for (u_int i = self->length - 1; i > index; i--)
        {
            Queue_Set(self, i, Queue_Get(self, i - 1));
        }
    }
    Queue_Set(self, index, value);
}

TValue Queue_Remove(Queue *self, _int index)
{
    if (index >= self->length)
        return TNULL;
    TValue value = Queue_Get(self, index);
    if (index << 1 < self->length)
    {
        for (u_int i = index; i > 0; i--)
        {
            Queue_Set(self, i, Queue_Get(self, i - 1));
        }
        Queue_UnShift(self);
    }
    else
    {
        for (u_int i = index; i < self->length - 1; i++)
        {
            Queue_Set(self, i, Queue_Get(self, i + 1));
        }
        Queue_Pop(self);
    }
    return value;
}

void Queue_Clear(Queue *self)
{
    self->size = 1;
    self->array = Globe_Realloc(self->array, sizeof(TValue) * self->size);
    self->begin = 0;
    self->end = 0;
    self->length = 0;
}

Queue *Queue_New(Line *line, u_int size)
{
    Queue *self = Globe_Malloc(sizeof(Queue));
    Queue_Init(self, size);
    return self;
}

// String Queue_Join(Queue *self, TValue value)
// {
//     _byte buffer[64];
//     Array array;
//     Array_Init(&array, _byte);
//     for (u_int i = 0; i < self->length; i++)
//     {
//         TValue item = Queue_Get(self, i);
//         switch (item.category)
//         {
//         case T_INTEGER:
//         {
//             sprintf(buffer, "%d", item.value.integer);
//             for (u_int j = 0; buffer[j] != 0; j++)
//             {
//                 Array_Push(&array, buffer[j], _byte);
//             }
//             break;
//         }
//         case T_DECIMAL:
//         {
//             sprintf(buffer, "%f", item.value.decimal);
//             for (u_int j = 0; buffer[j] != 0; j++)
//             {
//                 Array_Push(&array, buffer[j], _byte);
//             }
//             break;
//         }
//         case T_STRING:
//         {
//             String *string = (String *)item.value.object;
//             for (u_int j = 0; j < string->length; j++)
//             {
//                 Array_Push(&array, string->array[j], _byte);
//             }
//             break;
//         }
//         }
//     }
//     Array_Push(&array, '\0', _byte);
//     String result;
//     String_Init(&result, array.pointer);
//     Array_Free(&array);
//     return result;
// }